﻿using System;
using System.Linq;
using Dpy.Math.Algebra;

namespace Dpy.Math.Geometry.Geometry2D
{
    public class Line2D : ILineGeometry2D
    {
        #region Fields

        private readonly Vector2D startPoint;

        private readonly Vector2D vector;

        #endregion


        #region Properties        

        public Vector2D this[double coordinate]
        {
            get
            {
                return this.startPoint + coordinate * this.vector;
            }
        }

        #endregion


        #region Constructors

        public Line2D(Vector2D startPoint, Vector2D secondPoint)
        {
            this.startPoint = startPoint;
            this.vector = secondPoint - this.startPoint;

            if (this.vector.IsZero())
            {
                throw new ArgumentException("In order to define Line2D you need to specify two differing points!");
            }
        }

        #endregion


        #region Methods

        public Vector2D GetPointProjection(Vector2D point, out double lineCoordinate)
        {
            lineCoordinate = ((point - this.startPoint) * this.vector) / (this.vector * this.vector);

            return this[lineCoordinate];
        }

        public bool TryGetLineCoordinate(Vector2D point, out double lineCoordinate)
        {
            lineCoordinate = 0;

            Vector2D coordinate = point - this.startPoint;
            if (!coordinate.ColinearsWith(this.vector))
            {
                return false;
            }

            lineCoordinate = coordinate[0] / this.vector[0];

            return true;
        }

        public bool IsParallelWith(Line2D other)
        {
            return this.vector.ColinearsWith(other.vector);
        }

        public bool IsOrthogonalWith(Line2D other)
        {
            return this.vector.IsOrthogonalWith(other.vector);
        }

        public static double GetAngleInRadians(Line2D a, Line2D b)
        {
            return Dpy.Math.Algebra.Vector.GetAngleInRadians(a.vector, b.vector);
        }

        public Vector2D IntersectWith(Line2D other)
        {
            double aCoordinate, bCoordinate;

            return Line2D.Intersect(this, other, out aCoordinate, out bCoordinate);
        }

        /// <summary>
        /// Intersects two lines.
        /// </summary>
        /// <param name="a">The first line.</param>
        /// <param name="b">The second line.</param>
        /// <param name="lineCoordinateOnA">The coordinate on line a. If two lines coincide lineCoodinateOnA is PositiveInfinity.</param>
        /// <param name="lineCoordinateOnB">The coordinate on line b. If two lines coincide lineCoodinateOnB is PositiveInfinity.</param>
        /// <returns>The intersection point. If two lines are parallel or coincide returns null.</returns>
        public static Vector2D Intersect(Line2D a, Line2D b, out double lineCoordinateOnA, out double lineCoordinateOnB)
        {
            lineCoordinateOnA = 0;
            lineCoordinateOnB = 0;

            LinearEquationsSystem equation = new LinearEquationsSystem(new double[,]{
                {a.vector[0], -b.vector[0]},
                {a.vector[1], -b.vector[1]},
            }, new double[] { 
            b.startPoint[0] - a.startPoint[0],
            b.startPoint[1] - a.startPoint[1],
            });

            if (equation.HasUniqueSolution)
            {
                lineCoordinateOnA = equation.Solution[0];
                lineCoordinateOnB = equation.Solution[1];

                Vector2D intersectionPoint = a[lineCoordinateOnA];

                return intersectionPoint;
            }
            else if (equation.IsSolvable)
            {
                lineCoordinateOnA = double.PositiveInfinity;
                lineCoordinateOnB = double.PositiveInfinity;
            }

            return null;
        }

        public Vector2D[] IntersectWith(Circle circle)
        {
            double[] lineCoordinates;

            return this.IntersectWith(circle, out lineCoordinates);
        }

        public Vector2D[] IntersectWith(Circle circle, out double[] lineCoordinates)
        {
            Vector2D ac = circle.Center - this.startPoint;

            lineCoordinates = Equation.SolveQuadratic(
                this.vector * this.vector,
                -2 * (this.vector * ac),
                ac * ac - circle.Radius * circle.Radius);

            if (lineCoordinates != null)
            {
                Vector2D[] intersections = new Vector2D[lineCoordinates.Length];

                for (int i = 0; i < lineCoordinates.Length; i++)
                {
                    intersections[i] = this[lineCoordinates[i]];
                }

                return intersections;
            }

            return new Vector2D[] { };
        }

        #endregion


        #region Overriden methods

        public override string ToString()
        {
            string listSeparator = Dpy.Math.Utility.CurrentCulture.TextInfo.ListSeparator;

            return string.Format("(({0}){1} ({2}))", this.startPoint, listSeparator, this.startPoint + this.vector);
        }

        #endregion

    }
}
